#pragma once

#include "buffer.hpp"
#include <functional>
#include <atomic>
#include <mutex>
#include <condition_variable>
#include <memory>
#include <thread>
namespace Log
{
    enum class AsyncSafeType // 这里用于控制生产消费buff是否是扩容
    {                        // 当数据很大时buff一直扩容是不安全的
        SAFE,
        UNSAFE
    };

    class AsyncLooper
    {
    public:
        using Functor = std::function<void(Buffer &)>;
        using ptr = std::shared_ptr<AsyncLooper>;

        AsyncLooper(const Functor &cb, AsyncSafeType type = AsyncSafeType::SAFE)
            : _running(true), _callback(cb), _thread(std::thread(&AsyncLooper::worker_loop, this)), _looper_type(type)
        {
        }

        ~AsyncLooper() { stop(); }

        void stop()
        {
            // exchange函数原子地将变量设置为一个新值，并返回原来的旧值。
            if (_running.exchange(false) == false) return; // 防止重复 stop     
            _cums_cond.notify_all();
            if (_thread.joinable())
                _thread.join();
        }

        void push(const char *data, size_t len)
        {
            // 由(主线程)业务线程进行
            if (_running == false)
                return;

            {
                std::unique_lock<std::mutex> lock(_mutex);
                // 生产者条件变量 wait ,  lambda如果生产缓冲区中可写数据大于len 返回true
                if (_looper_type == AsyncSafeType::SAFE) // 如果Asy是安全的则需要进行 判断 len的大小是否符合缓冲区的容量
                {
                    _prod_cond.wait(lock, [&]()
                                    { return _prod_buff.writeAbleSize() >= len; });
                }

                _prod_buff.push(data, len);
            }
            // 生产缓冲区有了数据后，便可以唤醒消费者
            /*使用 notify_all() 是为了确保：
              所有等待的线程在 _running == false 时都能退出；
              保证不会有线程永远阻塞在 wait 上。*/
            _cums_cond.notify_all();
        }

    private:
        void worker_loop()
        {
            // 由工作线程来进行
            while (1)
            {
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    // 如果生产缓冲区中仍然有数据存在，那么不能退出 而是要等到数据全部处理完全才可以退出
                    if (_running == false && _prod_buff.empty())
                        return;
                    // 消费者条件变量 wait ,   lambda如果生产缓冲区不为空(因为缓冲区有数据需要进行处理) 或者 _runing为false(这里我们需要唤醒所有的线程) 那么返回true
                    _cums_cond.wait(lock, [&]()
                                    { return !_prod_buff.empty() || !_running; });

                    _cums_buff.swap(_prod_buff);
                }
                // 唤醒生产者条件变量
                if (_looper_type == AsyncSafeType::SAFE)
                {
                    _prod_cond.notify_all();
                }
                // 通过回调函数 处理消费缓冲区中的数据
                _callback(_cums_buff);
                // 初始化消费缓冲区
                _cums_buff.reset();
            }
        }

    private:
        Functor _callback;
        AsyncSafeType _looper_type; // 用于控制缓冲区是否扩容
    private:
        std::mutex _mutex;
        std::atomic<bool> _running;         // 控制异步线程是否继续运行或安全退出，它是异步线程生命周期中的一个控制标志
        Buffer _prod_buff;                  // 生产缓冲区
        Buffer _cums_buff;                  // 消费缓冲区
        std::condition_variable _prod_cond; // 生产者条件变量
        std::condition_variable _cums_cond; // 消费者条件变量
        std::thread _thread;                // 工作线程,这里虽然只有一个线程但是后续可以引入线程池等待，符合扩展性，但是一个日志系统就不需要多个线程来实现
    };
}